﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Tomasulo_Simulator
{
    class Instruction
    {
        Processor.Operation operation;
        int regA, regB, regC, imm, pc;
        int cyclesRemainingForFetching, initialPC, predictedAddress;

        public Instruction(int cyclesRemainingForFetching, int initialPC)
        {
            this.cyclesRemainingForFetching = cyclesRemainingForFetching;
            this.initialPC = initialPC;
        }

        public void Update()
        {
            cyclesRemainingForFetching--;
            if (cyclesRemainingForFetching < 0)
                cyclesRemainingForFetching = 0;
        }

        public bool Ready
        {
            get { return cyclesRemainingForFetching == 0; }
        }

        public int Initialize(string instruction)
        {
            string[] input = instruction.Split(' ');
            operation = (Processor.Operation)Enum.Parse(typeof(Processor.Operation), input[0]); 
            input = input[1].Split(',');
            regA = input[0][1] - '0';
            switch (operation)
            {
                case Processor.Operation.LW:
                    regA = input[0][1] - '0';
                    regB = input[1][1] - '0';
                    imm = Convert.ToInt32(input[2]);
                    break;
                case Processor.Operation.SW:
                    regA = input[0][1] - '0';
                    regB = input[1][1] - '0';
                    imm = Convert.ToInt32(input[2]);
                    break;
                case Processor.Operation.JMP:
                    regA = input[0][1] - '0';
                    imm = Convert.ToInt32(input[1]);
                    break;
                case Processor.Operation.BEQ:
                    regA = input[0][1] - '0';
                    regB = input[1][1] - '0';
                    imm = Convert.ToInt32(input[2]);
                    break;
                case Processor.Operation.JALR:
                    regA = input[0][1] - '0';
                    regB = input[1][1] - '0';
                    break;
                case Processor.Operation.RET:
                    regA = input[0][1] - '0';
                    break;
                case Processor.Operation.ADD:
                    regA = input[0][1] - '0';
                    regB = input[1][1] - '0';
                    regC = input[2][1] - '0';
                    break;
                case Processor.Operation.ADDI:
                    regA = input[0][1] - '0';
                    regB = input[1][1] - '0';
                    imm = Convert.ToInt32(input[2]);
                    break;
                case Processor.Operation.NAND:
                    regA = input[0][1] - '0';
                    regB = input[1][1] - '0';
                    regC = input[2][1] - '0';
                    break;
                case Processor.Operation.MUL:
                    regA = input[0][1] - '0';
                    regB = input[1][1] - '0';
                    regC = input[2][1] - '0';
                    break;
                case Processor.Operation.DIV:
                    regA = input[0][1] - '0';
                    regB = input[1][1] - '0';
                    regC = input[2][1] - '0';
                    break;
            }

            predictedAddress = initialPC + 1;

            if (operation == Processor.Operation.JMP)
                predictedAddress = initialPC + 1 + imm + Processor.processor.Regs[regA];
            if (operation == Processor.Operation.JALR)
                predictedAddress = Processor.processor.Regs[regB];
            if (operation == Processor.Operation.RET)
                predictedAddress = Processor.processor.Regs[regA];
            if (operation == Processor.Operation.BEQ && imm < 0)
                predictedAddress = initialPC + imm + 1;

            return predictedAddress;
        }

        public bool Issue()
        {
            if (!Ready)
                return false;
            if (Processor.processor.RS.Count < Processor.processor.RSSize && !Processor.processor.ROB.IsFull())
            {
                Processor.processor.RS.Add(new ReservationStation(this, Processor.processor.RS.Count));
                int destination;
                if (operation == Processor.Operation.SW)
                {
                    destination = regB + imm;
                }
                else if (operation == Processor.Operation.BEQ || operation == Processor.Operation.JALR
                    || operation == Processor.Operation.JMP || operation == Processor.Operation.RET)
                {
                    destination = predictedAddress;
                }
                else
                {
                    destination = regA;
                }
                
                Processor.processor.ROB.AddEntry(new ReorderBufferEntry(operation, destination, predictedAddress));
                return true;
            }
            return false;
        }
    }
}
